package com.ruoyi.web.controller;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.project.domain.entity.ProjectOperLog;
import com.ruoyi.project.service.IProjectOperLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Aspect
@Component
public class ProjectLogAspect {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Resource
    private IProjectOperLogService projectOperLogService;

    /**
     * 环绕通知：拦截所有标记了 @LogApi 注解的方法
     */
    @Around("@annotation(logApi)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogApi logApi) throws Throwable {
        long start = System.currentTimeMillis();

        // 获取请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        // 构建日志实体
        ProjectOperLog operLog = new ProjectOperLog();
        operLog.setRequestId(java.util.UUID.randomUUID().toString());

        // 从注解中获取模块名（推荐方式）
        operLog.setModuleName(logApi.module());

        operLog.setMethodName(joinPoint.getSignature().getName());
        operLog.setRequestUrl(request.getRequestURL().toString());
        operLog.setRequestMethod(request.getMethod());
        operLog.setRequestIp(request.getRemoteAddr());
        operLog.setUri(request.getRequestURI());

        // 获取当前登录用户信息
        String nickName = "匿名用户";
        String username = "anonymous";
        try {
            var loginUser = SecurityUtils.getLoginUser();
            if (loginUser != null) {
                nickName = loginUser.getUser().getNickName();
                username = loginUser.getUsername();
            }
        } catch (Exception e) {
            nickName = "系统";
            username = "system";
        }
        operLog.setOperator(nickName);

// ✅ 安全构建请求参数（避免非法 JSON）
        String requestParamsJson = "{}";
        try {
            Object[] args = joinPoint.getArgs();
            List<Object> safeArgs = new ArrayList<>();

            for (Object arg : args) {
                if (arg == null) continue;

                Class<?> clazz = arg.getClass();

                // 排除 Spring、Servlet、文件、输入流等非业务对象
                if (clazz.getName().startsWith("org.springframework") ||
                        clazz.getName().startsWith("javax.servlet") ||
                        arg instanceof org.springframework.web.multipart.MultipartFile ||
                        arg instanceof java.io.InputStream) {
                    continue;
                }

                // 特殊处理：如果是基本类型或包装类、String、Date，直接保留
                if (arg instanceof Number || arg instanceof Boolean || arg instanceof Character ||
                        arg instanceof String || arg instanceof Date) {
                    safeArgs.add(arg);
                }
                // 如果是普通 POJO（如查询对象），尝试用 ObjectMapper 序列化后再反序列化为 Map，去掉代理和复杂结构
                else {
                    try {
                        // 先序列化成 JSON 字符串，再反序列化为 LinkedHashMap（剥离代理、循环引用等问题）
                        String json = objectMapper.writeValueAsString(arg);
                        Object cleanObj = objectMapper.readValue(json, Object.class); // 转为通用结构
                        safeArgs.add(cleanObj);
                    } catch (Exception e) {
                        // 若仍失败，记录类名和 toString()
                        Map<String, String> fallback = new HashMap<>();
                        fallback.put("type", clazz.getSimpleName());
                        fallback.put("value", "序列化失败: " + e.getMessage());
                        fallback.put("toString", arg.toString());
                        safeArgs.add(fallback);
                    }
                }
            }

            if (!safeArgs.isEmpty()) {
                requestParamsJson = objectMapper.writeValueAsString(safeArgs);
            } else {
                requestParamsJson = "{}";
            }
        } catch (Exception e) {
            // 最终兜底：确保是合法 JSON
            requestParamsJson = "{\"error\": \"日志序列化失败\", \"detail\": \"" +
                    e.getMessage().replaceAll("\"", "'") + "\"}";
        }

        operLog.setRequestParams(requestParamsJson);

        Object result = null;
        try {
            // 执行目标方法
            result = joinPoint.proceed();

            // 序列化返回结果
            try {
                if (result != null) {
                    // 同样做“序列化 -> 反序列化”清洗
                    String json = objectMapper.writeValueAsString(result);
                    Object cleanResult = objectMapper.readValue(json, Object.class);
                    operLog.setResponseResult(objectMapper.writeValueAsString(cleanResult));
                } else {
                    operLog.setResponseResult("null");
                }
            } catch (Exception e) {
                operLog.setResponseResult("{\"error\": \"响应清洗失败\", \"detail\": \"" + e.getMessage().replaceAll("\"", "'") + "\"}");
            }

            return result;
        } catch (Throwable throwable) {
            // 捕获异常并记录
            try {
                operLog.setResponseResult("异常: " + throwable.getMessage());
            } catch (Exception e) {
                operLog.setResponseResult("异常信息记录失败");
            }
//            operLog.setStatus("0"); // 失败
            throw throwable; // 继续抛出异常
        } finally {
            long elapsedTime = System.currentTimeMillis() - start;
//            operLog.setCostTime((int) elapsedTime);
            operLog.setCreateTime(new Date());

            // 保存日志（建议异步，此处为同步示例）
            projectOperLogService.insertProjectOperLog(operLog);
        }
    }
}